using System;
using System.Collections.Generic;
using System.Diagnostics;
using BepuUtilities.Memory;
using System.Runtime.CompilerServices;

namespace BepuUtilities.Collections
{
    /// <summary>
    /// 容器支持恒定时间添加和删除,同时保持快速迭代时间。
    /// 以牺牲安全为代价提供非常直接的信息访问。
    /// </summary>
    /// <remarks>
    /// <para>
    /// 使用此类型时要非常小心。它在性能的祭坛上牺牲了很多;几个值得注意的问题包括：
    /// 它是一种值类型,复制它会在不加注意的情况下破坏东西,
    /// 它不能被有效地缺省构造,
    /// 它将内部结构暴露给用户修改,
    /// 它很少检查输入是否有错误,
    /// 枚举器不检查枚举中期修改,
    /// 它允许不安全的添加,如果用户不管理容量可能会中断,
    /// 它在抽象的存储器二进制大对象之上工作,该抽象的存储器二进制大对象在内部可能是可以被拉动的指针,
    /// 它不(也不能)检查所提供的内存是否返回到它来自的同一池。
    /// </para>
    /// <para>请注意,实现非常简单。它在冲突率非常低的假设下使用单步线性探测。
    /// 建议使用较大的表容量;这会牺牲一些内存以换取简单性和运行时性能。</para></注释>
    /// <typeparam name="T">容器保存的元素类型。</typeparam>
    /// <typeparam name="TEqualityCompeller">使用的相等测试器和哈希计算器的类型。</typeparam>
    public struct QuickSet<T, TEqualityComparer> where TEqualityComparer : IEqualityComparerRef<T> where T : unmanaged
    {
        /// <summary>
        /// 获取集合中的元素数。
        /// </summary>
        public int Count;
        /// <summary>
        /// 用于对哈希执行快速模运算的掩码。要求表跨度是2的幂。
        /// </summary>
        public int TableMask;
        /// <summary>
        /// 表的期望大小相对于键/值范围的大小,以2的幂表示。表容量目标将是elementCapacityTarget*2^TablePowerOffset。
        /// </summary>
        public int TablePowerOffset;

        /// <summary>
        /// 备份集合桌子的内存。值是根据EqualityCompeller的散列函数分发的。
        /// 包含0的插槽未使用,并且没有指向任何内容。包含较高值的窗等于1加上跨度中元素的索引。
        /// </summary>
        public Buffer<int> Table;

        /// <summary>
        /// 包含集合元素的后备存储器。
        /// 从0到COUNT-1的索引保存实际数据。所有其他数据均未定义。
        /// </summary>
        public Buffer<T> Span;

        /// <summary>
        /// 用于比较和散列元素的相等比较器。
        /// </summary>
        public TEqualityComparer EqualityComparer;


        /// <summary>
        /// 获取或设置集的列表表示形式中给定索引处的元素。
        /// </summary>
        /// <param name="index">从中获取元素的索引。</param>
        /// <return>集合中给定索引处的元素。</return>
        public ref T this[int index]
        {
            // TODO：检查内联
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                Debug.Assert(index >= 0 && index < Count, "Index should be within the set's size.");
                return ref Span[index];
            }
        }

        /// <summary>
        /// 创建新集。
        /// </summary>
        /// <param name="initialSpan">用作集合元素后备内存的范围。</param>
        /// 用作表的后备内存的<param name="initialTableSpan">范围。必须清零。</param>
        /// <param name="Compeller">用于集合的比较器。</param>
        /// <param name="tablePowerOffset">表的目标大小相对于存储的元素数。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public QuickSet(ref Buffer<T> initialSpan, ref Buffer<int> initialTableSpan, TEqualityComparer comparer, int tablePowerOffset = 2)
        {
            ValidateSpanCapacity(ref initialSpan, ref initialTableSpan);
            Span = initialSpan;
            Table = initialTableSpan;
            Count = 0;
            TableMask = Table.Length - 1;
            TablePowerOffset = tablePowerOffset;
            EqualityComparer = comparer;
            Debug.Assert(EqualityComparer != null);
            ValidateTableIsCleared(ref initialTableSpan);
        }

        /// <summary>
        /// 使用默认构造的相等比较器创建新集。
        /// </summary>
        /// <param name="initialSpan">用作集合元素后备内存的范围。</param>
        /// 用作表的后备内存的<param name="initialTableSpan">范围。必须为零。</param>
        /// <param name="tablePowerOffset">表的目标大小相对于存储的元素数。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public QuickSet(ref Buffer<T> initialSpan, ref Buffer<int> initialTableSpan, int tablePowerOffset = 2)
            : this(ref initialSpan, ref initialTableSpan, default, tablePowerOffset)
        {
        }

        /// <summary>
        /// 创建新集。
        /// </summary>
        /// <param name="pool">要从中拉取跨度的池。</param>
        /// <param name="initialCapacity">键和值的初始目标大小。初始缓冲区的大小至少与initialCapacity相同。</param>
        /// <param name="tableSizePower">相对于初始容量的目标容量,其幂为2。初始表缓冲区的大小将至少是初始容量的2^tableSizePower倍。</param>
        /// <param name="Compeller">要在集合中使用的比较器。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public QuickSet(int initialCapacity, int tableSizePower, IUnmanagedMemoryPool pool, TEqualityComparer comparer)
        {
            pool.TakeAtLeast<T>(initialCapacity, out var span);
            pool.TakeAtLeast<int>(span.Length << tableSizePower, out var tableSpan);
            // 不能保证桌子是干净的;把它清理干净。
            tableSpan.Clear(0, tableSpan.Length);
            this = new QuickSet<T, TEqualityComparer>(ref span, ref tableSpan, comparer, tableSizePower);
        }

        /// <summary>
        /// 使用默认构造的比较器创建新集。
        /// </summary>
        /// <param name="pool">要从中拉取跨度的池。</param>
        /// <param name="initialCapacity">键和值的初始目标大小。初始缓冲区的大小至少与initialCapacity相同。</param>
        /// <param name="tableSizePower">相对于初始容量的目标容量,其幂为2。初始表缓冲区的大小将至少是初始容量的2^tableSizePower倍。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public QuickSet(int initialCapacity, int tableSizePower, IUnmanagedMemoryPool pool)
            : this(initialCapacity, tableSizePower, pool, default)
        {
        }

        /// <summary>
        /// 使用默认构造的比较器创建新集。
        /// </summary>
        /// <param name="pool">要从中拉取跨度的池。</param>
        /// <param name="initialCapacity">键和值的初始目标大小。初始缓冲区的大小至少与initialCapacity相同。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public QuickSet(int initialCapacity, IUnmanagedMemoryPool pool)
            : this(initialCapacity, 2, pool, default)
        {
        }

        /// <summary>
        /// 将集合的后备内存范围换成新的范围。
        /// 如果新跨度较小,则会截断集合的计数,并删除额外的元素。
        /// 旧跨度不会被清除或返回到任何池;如果需要将其池化或清除,则用户必须处理它。
        /// </summary>      
        /// <param name="newSpan">用于元素的新范围。</param>
        /// <param name="newTableSpan">用于表的新span。必须为零。</param>
        /// <param name="oldSpan">用于元素的上一个范围。</param>
        /// <param name="oldTableSpan">表使用的上一个跨度。</param>
        public void Resize(ref Buffer<T> newSpan, ref Buffer<int> newTableSpan, out Buffer<T> oldSpan, out Buffer<int> oldTableSpan)
        {
            ValidateSpanCapacity(ref newSpan, ref newTableSpan);
            ValidateTableIsCleared(ref newTableSpan);
            var oldSet = this;
            Span = newSpan;
            Table = newTableSpan;
            Count = 0;
            TableMask = newTableSpan.Length - 1;
            var newCount = oldSet.Count > newSpan.Length ? newSpan.Length : oldSet.Count;

            // 不幸的是,我们真的不能直接复制;后备表依赖于模运算。
            // 从技术上讲,我们可以复制常规套装,然后依靠部分添加来打理睡觉,但是废话连篇！
            // 无论如何,只要有可能,都应该尽量避免在集合和字典上调整大小。不是很快。
            for (int i = 0; i < newCount; ++i)
            {
                // 我们假设ref add将在这里合理地内联。这实际上不能保证,但我们会咬紧牙关。
                // (从技术上讲,您可以在Unsafe.SizeOf<T>上进行分支,这应该会导致编译时专用的零开销实现……但是喵！)
                AddUnsafelyRef(ref oldSet.Span[i]);
            }
            oldSpan = oldSet.Span;
            oldTableSpan = oldSet.Table;

        }

        /// <summary>
        /// 按给定大小调整集的支持数组的大小。
        /// 如果新跨度较小,则会截断集合的计数,并删除额外的元素。
        /// </summary>
        /// <param name="newsize">新对象内存挡路的最小大小。实际大小可能更大。</param>
        /// <param name="pool">要从中获取跨度的池。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Resize(int newSize, IUnmanagedMemoryPool pool)
        {
            var targetCapacity = pool.GetCapacityForCount<T>(newSize);
            if (targetCapacity != Span.Length)
            {
                var oldSet = this;
                pool.TakeAtLeast<T>(newSize, out var newSpan);
                pool.TakeAtLeast<int>(newSpan.Length << TablePowerOffset, out var newTableSpan);
                // 不能保证从池中检索的表是干净的。把它清理干净！
                newTableSpan.Clear(0, newTableSpan.Length);
                Resize(ref newSpan, ref newTableSpan, out var oldSpan, out var oldTableSpan);
                oldSet.Dispose(pool);
            }
        }

        /// <summary>
        /// 将与集合关联的资源返回到池。仍包含在该集中的任何托管引用都将被清除(某些非托管资源也可能被清除)。
        /// </summary>
        /// <param name="pool">用于元素跨度的池。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Dispose(IUnmanagedMemoryPool pool)
        {
            pool.Return(ref Span);
            pool.Return(ref Table);
        }

        /// <summary>
        /// 确保集合有足够的空间来容纳指定数量的元素。
        /// </summary>     
        /// <param name="pool">用于跨度的池。</param>
        /// <param name="count">要保存的元素数。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void EnsureCapacity(int count, IUnmanagedMemoryPool pool)
        {
            if (count > Span.Length)
            {
                Resize(count, pool);
            }
        }

        /// <summary>
        /// 将内部缓冲区缩小到可接受的最小大小,并将旧缓冲区释放到池中。
        /// </summary>
        /// <param name="element">要添加的元素。</param>
        /// <param name="pool">用于跨度的池。</param>
        public void Compact(IUnmanagedMemoryPool pool)
        {
            Validate();
            var targetCapacity = pool.GetCapacityForCount<T>(Count);
            if (targetCapacity < Span.Length)
                Resize(Count, pool);
        }

        /// <summary>
        /// 获取表中元素的索引。
        /// </summary>
        /// <param name="element">要查找的元素。</param>
        /// <param name="tableIndex">重定向表中元素的索引,如果不存在,则为要添加该元素的位置的索引。</param>
        /// <param name="elementIndex">元素数组中元素的索引(如果存在);否则为-1。</param>
        /// <return>如果元素存在于集合中,则为True;如果不存在,则为False。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool GetTableIndices(ref T element, out int tableIndex, out int elementIndex)
        {
            Validate();
            // 表长保证是2的幂,因此模数是一个简单的二元运算。
            tableIndex = HashHelper.Rehash(EqualityComparer.Hash(ref element)) & TableMask;
            // 表中的0表示"未采用";所有其他值向上偏移1。也就是说,1实际上是索引0,2实际上是索引1,依此类推。
            // 这比使用负数进行标记更可取,因为清除缓冲区将包含全0。
            while ((elementIndex = Table[tableIndex]) > 0)
            {
                // 此表索引被采用。这是指定的元素吗？
                // 记住解码对象索引。
                if (EqualityComparer.Equals(ref Span[--elementIndex], ref element))
                {
                    return true;
                }
                tableIndex = (tableIndex + 1) & TableMask;
            }
            elementIndex = -1;
            return false;
        }

        /// <summary>
        /// 获取集合中元素的索引(如果存在)。
        /// </summary>
        /// <param name="element">要获取其索引的元素。</param>
        /// <return>如果元素存在于集合中,则为该元素的索引,否则为-1。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public int IndexOf(T element)
        {
            GetTableIndices(ref element, out int tableIndex, out int objectIndex);
            return objectIndex;
        }

        /// <summary>
        /// 获取集合中元素的索引(如果存在)。
        /// </summary>
        /// <param name="element">要获取其索引的元素。</param>
        /// <return>如果元素存在于集合中,则为该元素的索引,否则为-1。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public int IndexOfRef(ref T element)
        {
            GetTableIndices(ref element, out int tableIndex, out int objectIndex);
            return objectIndex;
        }

        /// <summary>
        /// 检查给定元素是否已属于该集合。
        /// </summary>
        /// <param name="element">要测试的元素。</param>
        /// <return>如果元素已属于该集合,则为True,否则为False。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool Contains(T element)
        {
            return GetTableIndices(ref element, out int tableIndex, out int objectIndex);
        }

        /// <summary>
        /// 检查给定元素是否已属于该集合。
        /// </summary>
        /// <param name="element">要测试的元素。</param>
        /// <return>如果元素已属于该集合,则为True,否则为False。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool ContainsRef(ref T element)
        {
            return GetTableIndices(ref element, out int tableIndex, out int objectIndex);
        }

        /// <summary>
        /// 将元素添加到集合。如果元素的一个版本(相同的散列码,比较器的‘等于’)已经存在,
        /// 它将被给定的版本替换。
        /// 在超出容量时不调整大小。
        /// </summary>
        /// <param name="element">要添加的元素。</param>
        /// <return>如果该元素已添加到集合中,则为True;如果该元素已存在并被替换,则为False。</return>
        // [MethodImpl(MethodImplOptions.AggressiveInlining)]//TODO：测试全链内联的性能。
        public bool AddAndReplaceUnsafelyRef(ref T element)
        {
            Validate();
            if (GetTableIndices(ref element, out int tableIndex, out int elementIndex))
            {
                // 已经到场了！
                Span[elementIndex] = element;
                return false;
            }

            // 它不在片场。加进去！
            ValidateUnsafeAdd();
            Span[Count] = element;
            // 使用编码-所有索引都偏移1,因为0表示"空"。
            Table[tableIndex] = ++Count;
            return true;
        }

        /// <summary>
        /// 将元素添加到集合。如果元素的一个版本(相同的散列码,比较器的‘等于’)已经存在,
        /// 它将被给定的版本替换。
        /// 在超出容量时不调整大小。
        /// </summary>
        /// <param name="element">要添加的元素。</param>
        /// <return>如果该元素已添加到集合中,则为True;如果该元素已存在并被替换,则为False。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool AddAndReplaceUnsafely(T element)
        {
            return AddAndReplaceUnsafelyRef(ref element);
        }

        /// <summary>
        /// 如果元素尚不存在,则将其添加到集合中。
        /// 在超出容量时不调整大小。
        /// </summary>
        /// <param name="element">要添加的元素。</param>
        /// <return>如果该元素已添加到集合中,则为True;如果该元素已存在,则为False。</return>
        // [MethodImpl(MethodImplOptions.AggressiveInlining)]//TODO：测试全链内联的性能。
        public bool AddUnsafelyRef(ref T element)
        {
            Validate();
            if (GetTableIndices(ref element, out int tableIndex, out int elementIndex))
            {
                // 已经到场了！
                return false;
            }

            // 它不在片场。加进去！
            ValidateUnsafeAdd();
            Span[Count] = element;
            // 使用编码-所有索引都偏移1,因为0表示"空"。
            Table[tableIndex] = ++Count;
            return true;
        }

        /// <summary>
        /// 如果元素尚不存在,则将其添加到集合中。
        /// 在超出容量时不调整大小。
        /// </summary>
        /// <param name="element">要添加的元素。</param>
        /// <return>如果该元素已添加到集合中,则为True;如果该元素已存在,则为False。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool AddUnsafely(T element)
        {
            return AddUnsafelyRef(ref element);
        }

        /// <summary>
        /// 将元素添加到集合。如果元素的一个版本(相同的散列码,比较器的‘等于’)已经存在,
        /// 它将被给定的版本替换。
        /// </summary>
        /// <param name="element">要添加的元素。</param>
        /// <param name="pool">用于跨度的池。</param>
        /// <return>如果该元素已添加到集合中,则为True;如果该元素已存在并被替换,则为False。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool AddAndReplaceRef(ref T element, IUnmanagedMemoryPool pool)
        {
            if (Count == Span.Length)
            {
                // 没有地方了;调整大小。
                Resize(Count * 2, pool);

                // 请注意,这是在找到任何索引之前进行测试的。
                // 如果我们只是在确定要添加它之后才调整大小,
                // 潜在的调整大小将使计算的索引无效。
            }
            return AddAndReplaceUnsafelyRef(ref element);
        }


        /// <summary>
        /// 如果元素尚不存在,则将其添加到集合中。
        /// </summary>
        /// <param name="element">要添加的元素。</param>
        /// <param name="pool">用于跨度的池。</param>
        /// <return>如果该元素已添加到集合中,则为True;如果该元素已存在,则为False。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool AddRef(ref T element, IUnmanagedMemoryPool pool)
        {
            if (Count == Span.Length)
            {
                // 没有地方了;调整大小。
                Resize(Count * 2, pool);

                // 请注意,这是在找到任何索引之前进行测试的。
                // 如果我们只是在确定要添加它之后才调整大小,
                // 潜在的调整大小将使计算的索引无效。
            }
            return AddUnsafelyRef(ref element);
        }


        /// <summary>
        /// 将元素添加到集合。如果元素的一个版本(相同的散列码,比较器的‘等于’)已经存在,
        /// 它将被给定的版本替换。
        /// </summary>
        /// <param name="element">要添加的元素。</param>
        /// <param name="pool">用于跨度的池。</param>
        /// <return>如果该元素已添加到集合中,则为True;如果该元素已存在并被替换,则为False。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool AddAndReplace(T element, IUnmanagedMemoryPool pool)
        {
            return AddAndReplaceUnsafelyRef(ref element);
        }


        /// <summary>
        /// 如果元素尚不存在,则将其添加到集合中。
        /// </summary>
        /// <param name="element">要添加的元素。</param>
        /// <param name="pool">用于跨度的池。</param>
        /// <return>如果该元素已添加到集合中,则为True;如果该元素已存在,则为False。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool Add(T element, IUnmanagedMemoryPool pool)
        {
            return AddRef(ref element, pool);
        }

        // 注意：尽管这是唯一的移除礼物,但它被命名为"FastRemove"而不是"Remove"的原因是
        // 稍后可能存在保存"删除"的命令。那将是一个非常狡猾的突破性变化。

        /// <summary>
        /// 根据元素的表和元素索引从字典中删除元素。仅当表和元素索引有效时才能使用。
        /// </summary>
        /// <param name="tableIndex">与要删除的现有元素关联的表项的索引。</param>
        /// <param name="elementIndex">要在连续元素数组中删除的现有元素的索引。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void FastRemove(int tableIndex, int elementIndex)
        {
            Debug.Assert(GetTableIndices(ref Span[elementIndex], out var debugTableIndex, out var debugElementIndex) && debugTableIndex == tableIndex && debugElementIndex == elementIndex,
                 "The table index and element index used to directly remove must match an actual element.");

            // 添加和删除必须同时维护一个属性：
            // 所有项都位于它们所需的索引处(由散列定义),或者它们都包含在从所需索引起顺时针方向连续的挡路中。
            // 移除器通过顺时针搜索查找可以向后移动的项目来填补它们造成的空白。
            // 顺时针搜索要填满此位置的项目。搜索必须继续,直到找到缺口。
            int moveCandidateIndex;
            int gapIndex = tableIndex;
            // 顺时针搜索。
            while ((moveCandidateIndex = Table[tableIndex = (tableIndex + 1) & TableMask]) > 0)
            {
                // 这个插槽里有东西。它的实际指数是多少？
                --moveCandidateIndex;
                int desiredIndex = HashHelper.Rehash(EqualityComparer.Hash(ref Span[moveCandidateIndex])) & TableMask;

                // 如果将此元素移动到间隙,是否会更接近其实际索引？
                // 要找出答案,请计算距间隙的顺时针距离和距理想位置的顺时针距离。

                var distanceFromGap = (tableIndex - gapIndex) & TableMask;
                var distanceFromIdeal = (tableIndex - desiredIndex) & TableMask;
                if (distanceFromGap <= distanceFromIdeal)
                {
                    // 到空隙的距离小于或等于到理想位置的距离,所以只需移动到空隙即可。
                    Table[gapIndex] = Table[tableIndex];
                    gapIndex = tableIndex;
                }

            }
            // 清除拆卸留下的台面间隙。
            Table[gapIndex] = 0;
            // 如果删除的对象不是最后一个对象,则将最后一个元素交换到删除对象的元素数组索引中。
            --Count;
            if (elementIndex < Count)
            {
                Span[elementIndex] = Span[Count];
                // 在表中找到交换的对象并更新其索引。
                GetTableIndices(ref Span[elementIndex], out tableIndex, out int oldElementIndex);
                Table[tableIndex] = elementIndex + 1; // 记住编码！所有索引偏移1。
            }
            // 清除元素集中的最后一个槽。
            Span[Count] = default;
        }

        /// <summary>
        /// 如果某个元素属于该集合,则从该集合中删除该元素。
        /// 不保留集合中元素的顺序。
        /// </summary>
        /// <param name="element">要删除的元素。</param>
        /// <return>如果找到并删除了元素,则为True,否则为False。</return>
        public bool FastRemoveRef(ref T element)
        {
            Validate();
            // 找到它。
            if (GetTableIndices(ref element, out int tableIndex, out int elementIndex))
            {
                // 我们找到那个物体了！
                FastRemove(tableIndex, elementIndex);
                return true;
            }
            return false;
        }

        /// <summary>
        /// 如果某个元素属于该集合,则从该集合中删除该元素。
        /// 不保留集合中元素的顺序。
        /// </summary>
        /// <param name="element">要删除的元素。</param>
        /// <return>如果找到并删除了元素,则为True,否则为False。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool FastRemove(T element)
        {
            return FastRemoveRef(ref element);
        }


        /// <summary>
        /// 从集合中删除所有元素。
        /// </summary>
        public void Clear()
        {
            // 虽然当集合是稀疏时从集合中移除单个元素可能很吸引人,
            // 在整张桌子上使用暴力清理几乎总是更快。而且要简单得多！
            Table.Clear(0, Table.Length);
            Span.Clear(0, Count);
            Count = 0;
        }

        /// <summary>
        /// 从集合中移除所有元素,而不修改元素数组的内容。
        /// </summary>
        public void FastClear()
        {
            Table.Clear(0, Table.Length);
            Count = 0;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public unsafe static implicit operator Span<T>(in QuickSet<T, TEqualityComparer> set)
        {
            return new Span<T>(set.Span.Memory, set.Count);
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public unsafe static implicit operator ReadOnlySpan<T>(in QuickSet<T, TEqualityComparer> set)
        {
            return new ReadOnlySpan<T>(set.Span.Memory, set.Count);
        }

        public Enumerator GetEnumerator()
        {
            Validate();
            return new Enumerator(ref Span, Count);
        }


        public struct Enumerator : IEnumerator<T>
        {
            private readonly Buffer<T> backingArray;
            private readonly int count;
            private int index;

            public Enumerator(ref Buffer<T> backingArray, int count)
            {
                this.backingArray = backingArray;
                this.count = count;

                index = -1;
            }

            public T Current
            {
                get { return backingArray[index]; }
            }

            public void Dispose()
            {
            }

            object System.Collections.IEnumerator.Current
            {
                get { return Current; }
            }

            public bool MoveNext()
            {
                return ++index < count;
            }

            public void Reset()
            {
                index = -1;
            }
        }
        [Conditional("DEBUG")]
        static void ValidateSpanCapacity(ref Buffer<T> span, ref Buffer<int> tableSpan)
        {
            Debug.Assert(tableSpan.Length >= span.Length, "The table span must be at least as large as the element span.");
            Debug.Assert((tableSpan.Length & (tableSpan.Length - 1)) == 0, "QuickSets depend upon power of 2 backing table span sizes for efficient modulo operations.");
        }

        [Conditional("DEBUG")]
        private void Validate()
        {
            Debug.Assert(Span.Length != 0 && Table.Length != 0, "The QuickSet must have its internal buffers and pools available; default-constructed or disposed sets should not be used.");
            ValidateSpanCapacity(ref Span, ref Table);
        }

        [Conditional("DEBUG")]
        void ValidateUnsafeAdd()
        {
            Debug.Assert(Count < Span.Length, "Unsafe adders can only be used if the capacity is guaranteed to hold the new size.");
        }

        [Conditional("DEBUG")]
        void ValidateTableIsCleared(ref Buffer<int> span)
        {
            for (int i = 0; i < span.Length; ++i)
            {
                Debug.Assert(span[i] == 0, "The table provided to the set must be cleared.");
            }
        }



    }
}
